import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.common.SolrInputDocument;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler

import org.grails.simplesolr.SimpleSolr;
import org.grails.simplesolr.SimpleSolrQueryResponse;

class SimpleSolrGrailsPlugin {
	// the plugin version
	def version = "0.1"
	// the version or versions of Grails the plugin is designed for
	def grailsVersion = "1.3.7 > *"
	// the other plugins this plugin depends on
	def dependsOn = [:]
	// resources that are excluded from plugin packaging
	def pluginExcludes = [
		"grails-app/views/error.gsp",
		"grails-app/domain/**",
		"grails-app/Config.groovy",
		"grials-app/UrlMappings.groovy",
		"grails-app/Datasource.groovy"
	]

	// TODO Fill in these fields
	def author = "Adrien Guichard"
	def authorEmail = "guichard.adrien@gmail.com"
	def title = "Gails Simple Solr Plugin"
	def description = '''\\
This plugin provides grails domain object with Solr indexation throw annotations. It is heavily inspired by the grails Solr plugin. 
'''

	// URL to the plugin's documentation
	def documentation = "http://grails.org/plugin/simplesolr"

	def doWithWebDescriptor = { xml ->
		// TODO Implement additions to web.xml (optional), this event occurs before
	}

	def doWithSpring = {
		// TODO Implement runtime spring config (optional)
	}

	def doWithDynamicMethods = { ctx ->

		application.domainClasses.each { dc ->

			if(GrailsClassUtils.getStaticPropertyValue(dc.clazz, "enableSimpleSolrSearch")) {

				String loc = "x.addField(\"id\", y.class.name+\"-\"+y.id)\n" // loc: lines of code
				loc += "x.addField(\"doctype_s\", y.class.name)\n"
				loc += "List l = []\n"
				dc.clazz.declaredFields.each { f ->
					if(f.isAnnotationPresent(SimpleSolr.class)) {
						String[] solrFN = f.getAnnotation(SimpleSolr).solrFields()
						String[] classFN = f.getAnnotation(SimpleSolr).classFields()
						boolean[] isAOfA = f.getAnnotation(SimpleSolr).arrayOfArray()

						if(solrFN.size() == 1) {
							if(isAOfA != [] && isAOfA[0] == true) {
								String targetFN = (classFN != [])?classFN[0]:f.name
								loc += "y.${targetFN}*.each { l << it }\n"
								loc += "x.addField(\"${solrFN[0]}\", l)\n"
								loc += "l = []\n"
							} else {
								String targetFN = (classFN != [])?classFN[0]:f.name
								loc += "x.addField(\"${solrFN[0]}\", y.${targetFN})\n"
							}
						} else {
							if(isAOfA != []) {
								int i = 0
								for(String sfn:solrFN) {
									if(isAOfA[i] == false) {
										loc += "x.addField(\"${sfn}\", y.${classFN[i]})\n"
									} else {
										loc += "y.${classFN[i]}*.each { l << it }\n"
										loc += "x.addField(\"${sfn}\", l)\n"
										loc += "l = []\n"
									}
									i++
								}
							} else {
								int i = 0
								for(String sfn:solrFN) {
									loc += "x.addField(\"${sfn}\", y.${classFN[i]})\n"
									i++
								}
							}
						}
					}
					Script script = (new GroovyShell()).parse(loc)
					
					dc.metaClass.indexSimpleSolr << {
						def solrService = ctx.getBean("simpleSolrService");
						SolrServer server = solrService.getServer()
						SolrInputDocument doc = new SolrInputDocument()

						Binding binding = new Binding()
						binding.setVariable("x", doc)
						binding.setVariable("y", delegate)
						script.setBinding(binding)
						script.run()

						server.add(doc)
						server.commit()
					}

					dc.metaClass.indexSimpleSolrNoCommit << {
						long start1Solr = System.currentTimeMillis()
						def solrService = ctx.getBean("simpleSolrService");
						SolrServer server = solrService.getServer()
						long start2Solr = System.currentTimeMillis()
						SolrInputDocument doc = new SolrInputDocument()

						Binding binding = new Binding()
						binding.setVariable("x", doc)
						binding.setVariable("y", delegate)
						script.setBinding(binding)
						script.run()

						long start3Solr = System.currentTimeMillis()
						server.add(doc)
						long endSolr = System.currentTimeMillis()
						return server
					}

					dc.metaClass.deleteSimpleSolr << {
						def solrService = ctx.getBean("simpleSolrService");
						SolrServer server = solrService.getServer()
						server.deleteByQuery( "id:${delegate.class.name}-${delegate.id}");
						server.commit()
					}

					dc.metaClass.'static'.searchSimpleSolr << { query ->
						def solrService = ctx.getBean("simpleSolrService");
						SolrServer server = solrService.getServer()
						def solrQuery = (query instanceof SolrQuery) ? query : new SolrQuery( query )
						def className = (delegate.class.name == 'java.lang.Class') ? delegate.name : delegate.class.name
						solrQuery.addFilterQuery("doctype_s:${className}")

						SimpleSolrQueryResponse result = solrService.search(solrQuery)

						return result
					}

					if (GrailsClassUtils.getStaticPropertyValue(dc.clazz, "SimpleSolrAutoIndex") != null) {
						dc.metaClass.afterInsert << {
							if(delegate.SimpleSolrAutoIndex == true)
								delegate.indexSimpleSolr()
						}

						dc.metaClass.afterUpdate << {
							if(delegate.SimpleSolrAutoIndex == true)
								delegate.indexSimpleSolr()
						}

						dc.metaClass.afterDelete << {
							if(delegate.SimpleSolrAutoIndex == true)
								delegate.deleteSimpleSolr()
						}
					}
				}
			}
		}
	}
	def doWithApplicationContext = { applicationContext ->
		// TODO Implement post initialization spring config (optional)
	}

	def onChange = { event ->
		// TODO Implement code that is executed when any artefact that this plugin is
		// watching is modified and reloaded. The event contains: event.source,
		// event.application, event.manager, event.ctx, and event.plugin.
	}

	def onConfigChange = { event ->
		// TODO Implement code that is executed when the project configuration changes.
		// The event is the same as for 'onChange'.
	}
}
